package rs.etf.kdp.server;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.swing.JTextArea;


/**
 * Thread running on specified port and accepts new connections
 * @author Djordje Vukovic
 * @version 1.0
 * @created 20-May-2011 2:30:19 AM
 */
public class NewConnectionsListener extends Thread{

	public static long _id = 1000;
	private ServerSocket serverSocket;
	private int port;
	boolean loop = true;
	private JTextArea status;
	static ExecutorService jobManagerPool = Executors.newFixedThreadPool(20);
	Heartbeater hb;
	AvailableNodes nodesList;
	
	public NewConnectionsListener(int port, JTextArea txtStatus, Heartbeater hb, AvailableNodes nodes){
		this.port = port;
		status = txtStatus;
		this.hb = hb;
		nodesList = nodes;
		
		Properties jobId = new Properties();
		FileInputStream propFile = null;
		try {
			propFile = new FileInputStream("res/jobid.properties");
		} catch (FileNotFoundException e) {
			status.append("\nUnable to load properties file for job id");
			return;
		}	
		try {
			jobId.load(propFile);
		} catch (IOException e) {
			status.append("\nUnable to assign file to properties object");
			return;
		}
		
		String lastId = jobId.getProperty("jobid");
		long longLastId = Long.parseLong(lastId);
		_id = longLastId;
		
		
	}

	public void closeServerSocket()
	{
		try
		{
			serverSocket.close();
		} catch(IOException ex)
		{
			status.append("\nError wile closing server socket");
		} catch (Exception e)
		{
			
		}
	}
	
	public void storeId()
	{
		Properties newId = new Properties();
		try {
			newId.load(new FileInputStream("res/jobid.properties"));
		} catch (FileNotFoundException e) {
			status.append("\nFileNotFound in finalize");
			//return;
		} catch (IOException e) {
			status.append("\nUnable to load properties file in finalize");
			//return;
		}
		
		Long currentId = new Long(_id);
		newId.setProperty("jobid", currentId.toString());
		
		try {
			newId.store(new FileOutputStream("res/jobid.properties"), null);
		} catch (FileNotFoundException e) {
			status.append("\nFileNotFound in finalize while saving");
		} catch (IOException e) {
			status.append("\nException while saving property file in finalize");
		}
	}
	
	public void finalize()
	{
		storeId();
		jobManagerPool.shutdown();
		try
		{
			serverSocket.close();
		} catch(IOException ex)
		{
			status.append("\nError wile closing server socket");
		}
	}
	public void run()
	{
		
		try {
			status.append("\nStarting server socket on port "+ port + " optimal number is " + ServerGUI.optimalNumberOfComponents);
			serverSocket = new ServerSocket(port);
		} catch (IOException e) {
			status.append("\n Unable te start server socket on port " + port);
			return;
		}

		while(loop)
		{
			status.append("\nWaiting...");
			Socket newNode;

			try {
				newNode = serverSocket.accept();
			}
			catch (IOException e) {
				status.append("\nError while accepting connection");
				return;
			}

			status.append("\nNew connection accepted ");
			serveConnection(newNode);
		}
		
	}
	
	private void serveConnection(Socket client)
	{
		status.append("\nConnection from " + client.getInetAddress().getHostAddress() + " " + client.getInetAddress().getHostName());
		
		ObjectOutputStream output;
		ObjectInputStream input;
		
		try {
			output = new ObjectOutputStream(client.getOutputStream());
			output.flush();
		} catch (IOException e) {
			status.append("\nJob Manager: Error while creating output stream");
			return;
		}
		
		try {
			input = new ObjectInputStream(client.getInputStream());
		} catch (IOException e) {
			status.append("\nJob Manager: Error while creating input stream");
			return;
		}
		//receive initial message
		String initMessage;
		
		try {
			initMessage = (String)input.readObject();
		} catch (IOException e) {
			status.append("\nJob Manager: Error while reading first message");
			return;
		} catch (ClassNotFoundException e) {
			status.append("\nJob Manager: ClassNotFound while reading first message");
			return;
		}
		
		String[] firstMessageTokens = initMessage.split(" ");
		if(firstMessageTokens[0].equals("client"))
		{
			/// CREATE CLIENT NODE
			status.append("New client registrated");
			//sendID(output);
			//Job job = recieveJob(input);
			//sendRecievedJobAck(output);
			//create job manager
			ConnectionWithClient clientConnection = new ConnectionWithClient(input, output, hb.getCurrentSimulations(), null, status, hb);
			clientConnection.start();
			//JobManager m = new JobManager(null, status, input, output, hb); 
			//new Thread(m).start();
			//jobManagerPool.submit(m);
		}
		else if(firstMessageTokens[0].equals("workstation"))
		{
			if(firstMessageTokens[1].equals("register"))
			{
				String addressOfWS = firstMessageTokens[3];
				String nameOfWS = firstMessageTokens[4];
				
				status.append("\nNew workstation with address " + addressOfWS + " and name " + nameOfWS + "  registrated");
				
				int allowedThreads = 0;
				try {
					allowedThreads = Integer.parseInt(firstMessageTokens[2]);
				} catch (NumberFormatException ne){
					status.append("\nUnable to parse number of possible threads on workstation");
				}
				
				if(!nodesList.checkName(nameOfWS))
				{
					try {
						output.writeObject("invalid_name");
					} catch (IOException e)
					{
						status.append("\nUnable to send rejection message to workstation");
					}
					status.append("\nName for workstation is rejected");
					return;
					
				}
			
				Node newWS = new Node(nameOfWS, allowedThreads, addressOfWS, input, output, hb);
				nodesList.insertNode(newWS);
				hb.addToNodesStatus(addressOfWS, nameOfWS);
				newWS.start();
				//send ack
				String ack = "workstation registrated";
				try {
					//output.writeObject(ack);
					newWS.sentStringMessage(ack);
				} catch (IOException e) {
					status.append("\nUnable to send acknowledgent message to workstation");
				}
			}
		}
		
	}

}